package org.openanypcao.cs.util;
  
import java.io.BufferedInputStream;   
import java.io.DataOutputStream;   
import java.io.File;   
import java.io.FileOutputStream;   
   
import java.io.IOException;   
   
import java.io.RandomAccessFile;   
   
import java.net.*;   
import java.util.logging.Level;
import java.util.logging.Logger;

import org.openanypcao.cs.core.LogManager;

/**  
 * 下载线程  
 * 原理：  
 *    根据传入的下载开始点以及文件下载结束点，利用HttpURLConnection的RANGE属性，  
 *    从网络文件开始下载，并结合判断是否下载的文件大小已经等于（文件下载结束点-下载开始点）， *      
 *    这里结合断点续传原理，可以更快、更有效的下载文件  
 */ 
public class GetFileThread extends Thread {   
       
    private Logger LogOutput = LogManager.getLogger(GetFileThread.class);
    
    long startPos, endPos;// 传入的文件下载开始、结束点    
    String currentFileThreadName;// 要带上完整的路径    
    String urlFile;// 网络文件地址    
    int currentThread;// 当前是那个线程，这主要是用于下载完成后将对应的检测标志设为true，表示下载完成    
       
       
    public GetFileThread(String urlFile, long startPos, long endPos,   
            String currentFileThreadName, int currentThread) {   
        this.startPos = startPos;   
        this.endPos = endPos;   
        this.currentFileThreadName = currentFileThreadName;   
        this.urlFile = urlFile;   
        this.currentThread = currentThread;   
    }   
   
    private boolean FileExist(String pathAndFile) {   
        File file = new File(pathAndFile);   
        if (file.exists())   
            return true;   
        else   
            return false;   
    }   
   
    private long FileSize(String pathAndFile) {   
        long fileSize = 0;   
        File filet = new File(pathAndFile);   
        fileSize = filet.length();   
        return fileSize;   
    }   
   
    private void FileRename(String fName, String nName) {   
        File file = new File(fName);   
        file.renameTo(new File(nName));   
        file.delete();   
    }   
   
    public void run() {   
        URL url = null;   
        HttpURLConnection httpURLConnection = null;   
           
        //三个流套接，参考书上P176    
        DataOutputStream dos = null;   
        BufferedInputStream bis = null;   
        FileOutputStream fos = null;   
           
        String localFile = currentFileThreadName; // 文件保存的地方及文件名，具体情况可以改    
           
        String localFile_tp = localFile + ".tp"; // 未下载完文件加.tp扩展名，以便于区别    
        long fileSize = 0;// 在断点续传中，用于取得当前文件已经下载的大小    
        int len = 0;   
        byte[] bt = new byte[1024];// 缓冲区    
        byte[] buffer=new byte[50*1024];   
        RandomAccessFile raFile = null;   
        long TotalSize = 0; // 当前块要下载的文件总大小    
        try {   
            url = new URL(urlFile);   
            httpURLConnection = (HttpURLConnection) url.openConnection();   
               
            TotalSize = endPos - startPos;// 取得要该块文件实际要写的大小    
   
            long downSize = 0;// 已经下载的大小    
            // 确定临时文件是否存在    
            if (FileExist(localFile_tp)) // 采用断点续传，这里的依据是看下载文件是否在本地有.tp有扩展名同名文件    
            {   
                LogOutput.log(Level.CONFIG, "文件续传中...");   
                   
                fileSize = new File(localFile_tp).length();// 取得已经下载的大小，    
                                                            // 以便确定随机写入的位置    
                downSize = fileSize;// 下载大小    
                fileSize = fileSize + startPos;// 取得文件开始写入点    
                   
                LogOutput.log(Level.CONFIG, "已近下载了：" + downSize);   
                LogOutput.log(Level.CONFIG, "写入点：" + fileSize);   
                   
                /**  
                 * httpURLConnection属性的设置一定要在得到输入流之前，否则会报已经连接的错误  
                 */   
                   
                // 设置断点续传的开始位置    
                httpURLConnection.setRequestProperty("RANGE", "bytes="   
                        + fileSize + "-");   
                   
                // 设置接受信息    
                httpURLConnection.setRequestProperty("Accept",   
                        "image/gif,image/x-xbitmap,application/msword,*/*");   
                   
                raFile = new RandomAccessFile(localFile_tp, "rw"); // 随机方位读取    
                raFile.seek(downSize); // 定位指针到fileSize位置    
                bis = new BufferedInputStream(httpURLConnection   
                        .getInputStream());   
                while ((len = bis.read(bt)) > 0) {   
                    if (downSize < (endPos - startPos)) {   
                        downSize = downSize + len;   
                        if (downSize > (endPos - startPos)) {   
                            len = (int) ((endPos - startPos) - (downSize - len));   
                        }   
                        raFile.write(bt, 0, len);   
                    } else   
                        break;   
                }   
                LogOutput.log(Level.CONFIG, "文件续传接收完毕！");   
            }    
               
            else if (!FileExist(localFile))// 采用原始下载，但保证该文件没有下载    
            {   
                // 设置断点续传的开始位置    
                httpURLConnection.setRequestProperty("RANGE", "bytes="   
                        + startPos + "-");   
                bis = new BufferedInputStream(httpURLConnection   
                        .getInputStream());   
                fos = new FileOutputStream(localFile_tp); // 没有下载完毕就将文件的扩展名命名.tp    
                dos = new DataOutputStream(fos);   
                LogOutput.log(Level.INFO, "正在接收文件...");   
                   
                while ((len = bis.read(bt)) > 0) {   
                    if (downSize < (endPos - startPos))// 确定没有下载完毕    
                    {   
                        downSize = downSize + len;   
                        if (downSize > (endPos - startPos))// 如果当前下载的加上要下载的(缓冲区)已经超过总大小    
                        {   
                            len = (int) ((endPos - startPos) - (downSize - len));//计算余下的量下载    
                        }   
                        dos.write(bt, 0, len);// 写文件    
                    } else   
                        break;   
                }   
            }   
            if (bis != null)   
                bis.close();   
            if (dos != null)   
                dos.close();   
            if (fos != null)   
                fos.close();   
            if (raFile != null)   
                raFile.close();   
               
            if (FileSize(localFile_tp) == TotalSize) // 下载完毕后，将文件重命名    
            {   
                FileRename(localFile_tp, localFile);   
                   
            }   
            //MultiThreadGetFile.checkList[currentThread] = true;   
        } catch (Exception e) {   
            try {   
                if (bis != null)   
                    bis.close();   
                if (dos != null)   
                    dos.close();   
                if (fos != null)   
                    fos.close();   
                if (raFile != null)   
                    raFile.close();   
            } catch (IOException f) {   
                f.printStackTrace();   
            }   
            e.printStackTrace();   
        }   
    }   
}
